/**
 * visTopoClustering.cpp
 * ubuntu8.04,g++4.3.2
 * 18/3/2009,VISUALIZATION_CGCAD_THSS_THU_BJ
 * Author : Xinlai,Lu
 *
 * Modification History:
 *
 */

#include "visFlowGuided.h"

namespace visualization
{
void visFlowGuided::groupCriticalPoints()
{
	const double THRESHOLD = 3;
	const size_t TYPENUM = 5;
	const Type Tp[TYPENUM] = {ATTRACTINFOCUS, REPELLINGFOCUS, 
														ATTRACTINGNODE, REPELLINGNODE, 
														CENTER
													 };
	vector<criticalPoint> tmp = criticalPoints;
	criticalPoints.clear();

	for(size_t i = 0; i < tmp.size(); i++)
	{
		vector<criticalPoint> sameGroup;
		sameGroup.push_back(tmp[i]);
		for(size_t j = i + 1; j < tmp.size(); j++)
		{
			double dist = tmp[i].position.Distance(tmp[j].position);
			if(dist < THRESHOLD)
			{
				sameGroup.push_back(tmp[j]);
				tmp.erase(tmp.begin() + j);
				j--;
			}
		}
		Point position(0, 0, 0);
		size_t type[TYPENUM];
		for(size_t l = 0; l < TYPENUM; l++)type[l] = 0;
		for(size_t k = 0; k < sameGroup.size(); k++)
		{
			position += sameGroup[k].position;
			type[sameGroup[k].type]++;
		}
		size_t theType = 0;
		for(size_t m = 0; m < TYPENUM - 1; m++)
		{	if(type[m] < type[m+1]) theType = m + 1; }
		criticalPoint cp = {position/sameGroup.size(), Tp[theType]};
		criticalPoints.push_back(cp);
	}
}

void visFlowGuided::initializeTemplates()
{
	m_NearestPointForSaddles = new positionAndDistance[saddles.size()];
	m_NearestPoints = new positionAndDistance[criticalPoints.size()];
	cout << "Saddle number	:	" << saddles.size() << endl;
	cout << "Critical points number	:	" << criticalPoints.size() << endl;

	for(size_t i = 0; i < saddles.size(); i++){	m_NearestPointForSaddles[i].distance = DBL_MAX;	}
	for(size_t j = 0; j < criticalPoints.size(); j++)	{	m_NearestPoints[j].distance = DBL_MAX; }

	for(size_t i = 0; i < saddles.size(); i++)
	{
		for(size_t j = 0; j < saddles.size(); j++)
		{
			if(i == j)continue;
			processSaddlesAndSaddles(i, j);
		}
		for(size_t j = 0; j < criticalPoints.size(); j++)
		{
			processSaddlesAndOthers(i, j);
		}
	}
	for(size_t i = 0; i < criticalPoints.size(); i++)
	{
		for(size_t j = 0; j < saddles.size(); j++)
		{
			processOthersAndSaddles(i, j);
		}
		for(size_t j = 0; j < criticalPoints.size(); j++)
		{
			if(i == j) continue;
			processOthersAndOthers(i, j);
		}
	}

	m_IntervalSaddles = new double[saddles.size()];
	m_IntervalCriticalPoints = new double[criticalPoints.size()];
	for(size_t i = 0; i < saddles.size(); i++)
	{
		m_IntervalSaddles[i] = 2;
	}
	for(size_t i = 0; i < criticalPoints.size(); i++)
	{
		//{ATTRACTINFOCUS, REPELLINGFOCUS, ATTRACTINGNODE, REPELLINGNODE, CENTER};
		if(ATTRACTINGNODE == criticalPoints[i].type || REPELLINGNODE == criticalPoints[i].type)
		{
			m_IntervalCriticalPoints[i] = 10;
		}
		else
		{
			m_IntervalCriticalPoints[i] = 2;
		}
	}
}

void visFlowGuided::makeSources()
{
	const double INFLUENCE_ZONE = 0.45;
	for(size_t i = 0; i < saddles.size(); i++)
	{
		Point saddle(saddles[i].x, saddles[i].y, saddles[i].z);
		Point eigV1(saddles[i].eigenvec[0], saddles[i].eigenvec[1], 0);
		Point eigV2(saddles[i].eigenvec[2], saddles[i].eigenvec[3], 0);
		Point direction1 = (eigV1 + eigV2)/sqrt(2.0);
		Point direction2 = (eigV1 - eigV2)/sqrt(2.0);
		Point direction3 = direction1 * -1;
		Point direction4 = direction2 * -1;

		const double radius = INFLUENCE_ZONE * m_NearestPointForSaddles[i].distance;
		const double projection = m_IntervalSaddles[i]/sqrt(2.0);
		for(double step = projection; step < radius; step+=projection)
		{
			Point tmp1 = direction1 * step;
			Point tmp2 = direction2 * step;
			Point tmp3 = direction3 * step;
			Point tmp4 = direction4 * step;
			m_Sources.push_back(saddle+tmp1);
			m_Sources.push_back(saddle+tmp2);
			m_Sources.push_back(saddle+tmp3);
			m_Sources.push_back(saddle+tmp4);
		}
		m_Sources.push_back(saddle + direction1 * projection / 4);
		m_Sources.push_back(saddle + direction2 * projection / 4);
		m_Sources.push_back(saddle + direction3 * projection / 4);
		m_Sources.push_back(saddle + direction4 * projection / 4);
	}
	for(size_t i = 0; i < criticalPoints.size(); i++)
	{
		const double radius = INFLUENCE_ZONE * m_NearestPoints[i].distance;
		Point center(criticalPoints[i].position);
		if(ATTRACTINGNODE == criticalPoints[i].type || REPELLINGNODE == criticalPoints[i].type)
		{
			const double thita = 2 * 3.14 / m_IntervalCriticalPoints[i];
			for(double j = 0; j < 2 * 3.14; j+=thita)
			{
				Point tmp(radius * cos(j), radius * sin(j));
				m_Sources.push_back(center + tmp);
			}
		}
		else
		{
			Point direction = m_NearestPoints[i].coordinates - criticalPoints[i].position;
			// Normalize
			direction /= m_NearestPoints[i].distance;
			const double radius = INFLUENCE_ZONE * m_NearestPoints[i].distance;
			for(double j = m_IntervalCriticalPoints[i]; j < radius; j+=m_IntervalCriticalPoints[i])
			{
				Point tmp = direction * j;
				m_Sources.push_back(center + tmp);
			}
		}
	}
}

intgResults *visFlowGuided::integrate()
{
	visStreamline stl(dataField);
	stl.setIntegrationDouble();
	size_t num = m_Sources.size();
	Point *sources = new Point[num];
	for(size_t i = 0; i < num; i++)	{sources[i] = m_Sources[i];}
	stl.setSources(sources, num);
	stl.setParameters(0.1, 100);
	delete []sources;
	return stl.integrate();
}

void visFlowGuided::processSaddlesAndSaddles(size_t i, size_t j)
{
	// Calculate the distance between saddles i and j.
	double dist = pow(saddles[i].x - saddles[j].x, 2) + 
								pow(saddles[i].y - saddles[j].y, 2) +
								pow(saddles[i].z - saddles[j].z, 2);
	dist = sqrt(dist);
	if(dist < m_NearestPointForSaddles[i].distance)
	{
		m_NearestPointForSaddles[i].distance = dist;
		m_NearestPointForSaddles[i].coordinates.setCoordinates( saddles[j].x,
																													  saddles[j].y,
																													  saddles[j].z
																												   );	
	}
}

void visFlowGuided::processSaddlesAndOthers(size_t i, size_t j)
{
	Point tmp(saddles[i].x, saddles[i].y, saddles[i].z);
	double dist = tmp.Distance(criticalPoints[j].position);
	if(dist < m_NearestPointForSaddles[i].distance)
	{
		m_NearestPointForSaddles[i].distance = dist;
		m_NearestPointForSaddles[i].coordinates = criticalPoints[j].position;
	}
}

void visFlowGuided::processOthersAndSaddles(size_t i, size_t j)
{
	Point tmp(saddles[j].x, saddles[j].y, saddles[j].z);
	double dist = tmp.Distance(criticalPoints[i].position);
	if(dist < m_NearestPoints[i].distance)
	{
		m_NearestPoints[i].distance = dist;
		m_NearestPoints[i].coordinates = tmp;
	}
}

void visFlowGuided::processOthersAndOthers(size_t i, size_t j)
{
	double dist = criticalPoints[i].position.Distance(criticalPoints[j].position);
	if(dist < m_NearestPoints[i].distance)
	{
		m_NearestPoints[i].distance = dist;
		m_NearestPoints[i].coordinates = criticalPoints[j].position;
	}
}

}//visualization

